/*
 * Copyright QLH 2023
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.feeyo.groundservice.composewidget.view.sidemenu

import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import com.feeyo.groundservice.composewidget.view.constant.ComposePosition
import kotlinx.coroutines.launch
import kotlin.math.roundToInt

/**
 * creator: QLH
 *
 * effect : 滑动删除控件
 *          slide to delete controls
 *
 * warning:
 * @param minScrollPosition 最小滚动位置(距离指定方向的顶点)
 *                          Minimum scroll position
 * @param maxScrollPosition 最大滚动位置(距离指定方向的顶点)
 *                          Maximum scroll position
 * @param backgroundContent 等待拖出的compose内容区域
 *                          Content of background
 * @param modifier 修饰
 * @param contentIsMove compose内容区域是否跟着移动
 *                      Does content follow
 * @param content compose内容区域,需要内容是横向可滚动的,并且需要自行给内容设置相应方向的PaddingValues或padding
 *                Content of compose
 *
 * @param onOpenOrClose 打开或者关闭时回调，告诉其他同类型组件当前滑块的状态和位置，方便做出响应，如只能同时打开一个
 *
 * 注意：需要设置content的宽度,高度默认是自适应的
 */
@Composable
fun SlideMenu(
    minScrollPosition: Dp,
    maxScrollPosition: Dp,
    backgroundContent: @Composable RowScope.(state: ChainScrollableComponentState) -> Unit,
    modifier: Modifier = Modifier,
    contentIsMove: Boolean = true,
    composePosition: ComposePosition = ComposePosition.End,
    onOpenOrClose: (state: ChainScrollableComponentState) -> Unit = {},
    content: @Composable RowScope.(state: ChainScrollableComponentState) -> Unit,
) {
    val scrollState = rememberScrollState()
    ChainScrollableComponent(
        minScrollPosition = minScrollPosition,
        maxScrollPosition = maxScrollPosition,
        chainContent = { state ->
            //根据方向进行设定 菜单布局的modifier
            val bgModifier = when (composePosition) {
                ComposePosition.Start, ComposePosition.End ->//水平方向滑动
                    Modifier.fillMaxHeight().width(maxScrollPosition)
                        .offset {
                            IntOffset(
                                state.getScrollPositionValue().roundToInt(),
                                0
                            )
                        }

                ComposePosition.Bottom, ComposePosition.Top ->//垂直方向滑动
                    Modifier.fillMaxWidth().height(maxScrollPosition)
                        .offset {
                            IntOffset(
                                0,
                                state.getScrollPositionValue().roundToInt()
                            )
                        }

            }
            Row(modifier = bgModifier) {
                backgroundContent(state)
            }
        },
        modifier = modifier.height(IntrinsicSize.Min),
        onScrollStop = scrollStop(scrollState, composePosition, onOpenOrClose),
        composePosition = composePosition,
        content = { state ->
            //根据方向进行设定 内容布局的modifier
            val contentModifier = when (composePosition) {
                ComposePosition.Start, ComposePosition.End ->//水平方向滑动
                    Modifier
                        .fillMaxWidth()
                        .let {
                            if (contentIsMove) {
                                it.offset {
                                    IntOffset(
                                        contentOffset(state, composePosition).roundToInt(),
                                        0
                                    )
                                }
                            } else
                                it
                        }
                        .horizontalScroll(scrollState)

                ComposePosition.Bottom, ComposePosition.Top ->//垂直方向滑动
                    Modifier
                        .fillMaxWidth()
                        .let {
                            if (contentIsMove) {
                                it.offset {
                                    IntOffset(
                                        0,
                                        contentOffset(state, composePosition).roundToInt()
                                    )
                                }
                            } else
                                it
                        }
                        .verticalScroll(scrollState)
            }
            Row(
                modifier = contentModifier
            ) {
                content(state)
            }
        }
    )
}

//停止拖动时,使用阈值检测是否展开还是收缩
private fun scrollStop(
    scrollState: ScrollState,
    orientation: ComposePosition,
    onOpenOrClose: (state: ChainScrollableComponentState) -> Unit
): (ChainScrollableComponentState) -> Unit =
    function@{ state ->
        val percentage = state.getScrollPositionPercentage()
        if (percentage == 1f || percentage == 0f) {
            onOpenOrClose(state)
            return@function
        }

        state.coroutineScope.launch {
            val startPositionValue = state.getScrollPositionValue()
            checkLastPositionWhenStopScroll(
                state,
                scrollState,
                orientation,
                percentage,
                startPositionValue,
                onOpenOrClose
            )
        }
    }

//检测滑动的回弹效果，阈值百分比为50%
private fun checkLastPositionWhenStopScroll(
    state: ChainScrollableComponentState,
    scrollState: ScrollState,
    orientation: ComposePosition,
    percentage: Float,
    startPositionValue: Float,
    onOpenOrClose: (state: ChainScrollableComponentState) -> Unit
) {
    when (orientation) {
        ComposePosition.End -> {
            if (percentage > 0.5f) {
                state.setScrollPositionWithAnimate(state.maxPx)
                //scrollState.animateScrollBy(state.maxPx - startPositionValue)
            } else {
                state.setScrollPositionWithAnimate(state.minPx)//其实也为0
                //scrollState.animateScrollBy(startPositionValue)
                //菜单打开时回调
                onOpenOrClose(state)
            }
        }
        ComposePosition.Start -> {
            if (percentage > 0.5f) {
                state.setScrollPositionWithAnimate(state.minPx)
                //scrollState.animateScrollBy(startPositionValue - state.minPx)
            } else {
                state.setScrollPositionWithAnimate(state.maxPx)//其实也为0
                //scrollState.animateScrollBy(startPositionValue)
            }
        }
        ComposePosition.Top -> {
            if (percentage > 0.5f) {
                state.setScrollPositionWithAnimate(state.minPx)
                //scrollState.animateScrollBy(startPositionValue - state.minPx)
            } else {
                state.setScrollPositionWithAnimate(state.maxPx)//其实也为0
                //scrollState.animateScrollBy(startPositionValue)
            }
        }
        ComposePosition.Bottom -> {
            if (percentage > 0.5f) {
                state.setScrollPositionWithAnimate(state.maxPx)
                //scrollState.animateScrollBy(state.maxPx - startPositionValue)
            } else {
                state.setScrollPositionWithAnimate(state.minPx)//其实也为0
                //scrollState.animateScrollBy(startPositionValue)
            }
        }
    }
}

//获取内容控件初始化的偏移，感觉初始化没有偏移，都为0才对，这块得注意点
private fun contentOffset(
    state: ChainScrollableComponentState,
    orientation: ComposePosition
): Float {
    return when (orientation) {
        ComposePosition.End, ComposePosition.Bottom -> state.getScrollPositionValue() - state.maxPx
        ComposePosition.Start, ComposePosition.Top -> state.getScrollPositionValue() - state.minPx
    }
}